﻿using Net.Event;
using Net.Share;
using Net.System;
using System.Collections.Generic;

namespace Net.Server
{
    public class SceneBase<Client> where Client : ClientPeerBase, new()
    {
        /// <summary>
        /// 场景名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 场景容纳人数
        /// </summary>
        public int Capacity { get; set; }
        /// <summary>
        /// 当前网络场景的玩家, 此字段不要使用Add, Remove进行调用
        /// </summary>
        public FastList<Client> Clients { get; set; } = new();
        /// <summary>
        /// 场景当前帧
        /// </summary>
        public uint Frame { get; set; }
        /// <summary>
        /// 备用操作, 当玩家被移除后速度比update更新要快而没有地方收集操作指令, 所以在玩家即将被移除时, 可以访问这个变量进行添加操作同步数据
        /// </summary>
        protected FastList<Operation> operations = new();
        /// <summary>
        /// 服务器对象
        /// </summary>
        public ServerBase<Client> Server { get; set; }
        /// <summary>
        /// 获取场景当前人数
        /// </summary>
        public int Count => Clients.Count;
        /// <summary>
        /// 场景(房间)人数是否已满？
        /// </summary>
        public bool IsFull => Clients.Count >= Capacity;
        /// <summary>
        /// 操作列表分段值, 当operations.Count的长度大于Split值时, 就会裁剪为多段数据发送 默认为500长度分段
        /// </summary>
        public int Split { get; set; } = 500;
        private readonly int hash;
        private int preFps, currFps;
        /// <summary>
        /// 场景帧数
        /// </summary>
        public int FPS => preFps;
        private ThreadGroup group;
        /// <summary>
        /// 当前场景线程组对象
        /// </summary>
        public ThreadGroup Group
        {
            get => group;
            set
            {
                group?.Remove(this);
                group = value;
                group?.Add(this); //当释放后Group = null;
            }
        }
        /// <summary>
        /// 场景计时器事件
        /// </summary>
        public TimerEvent Event { get; private set; } = new();
        /// <summary>
        /// 网络场景同步时间(帧同步间隔), 默认每33毫秒同步一次, 一秒同步30次, 可自己设置
        /// </summary>
        public uint TimeTick { get; set; } = 33;

        /// <summary>
        /// 构造网络场景
        /// </summary>
        public SceneBase()
        {
            hash = GetHashCode();
        }

        /// <summary>
        /// 添加网络主场景并增加主场景最大容纳人数
        /// </summary>
        /// <param name="capacity">主场景最大容纳人数</param>
        public SceneBase(int capacity) : this()
        {
            Capacity = capacity;
        }

        /// <summary>
        /// 添加网络场景并增加当前场景人数
        /// </summary>
        /// <param name="client">网络玩家</param>
        /// <param name="capacity">创建场景容纳人数</param>
        public SceneBase(Client client, int capacity) : this()
        {
            Capacity = capacity;
            AddPlayer(client);
        }

        internal void InitEvent(uint timeTick)
        {
            TimeTick = timeTick;
            Event.AddEvent("Update", timeTick, UpdateLock);
            Event.AddEvent("OnFPS", 1000L, OnFPS);
        }

        /// <summary>
        /// 添加玩家
        /// </summary>
        /// <param name="client"></param>
        public virtual void AddPlayer(Client client)
        {
            lock (this)
            {
                AddPlayerInternal(client);
            }
        }

        private void AddPlayerInternal(Client client)
        {
            if (client.SceneHash == hash)
                return;
            //如果已经在场景里面, 必须要先退出, 否则会发生一个玩家在多个场景的重大问题, 当玩家在多个场景后, 客户端被移除就找不到这个玩家进行移除,就会导致内存泄露问题
            if (client.Scene is SceneBase<Client> preScene)
                preScene.Remove(client);
            client.SceneName = Name;
            client.Scene = this;
            client.Group = Group;
            Clients.Add(client);
            OnEnter(client);
            client.OnEnter();
            client.SceneHash = hash;
        }

        /// <summary>
        /// 当进入场景的玩家
        /// </summary>
        /// <param name="client"></param>
        public virtual void OnEnter(Client client) { }

        /// <summary>
        /// 当开始退出场景，当调用此方法时client还在Clients属性里面
        /// </summary>
        /// <param name="client"></param>
        public virtual void OnBeginExit(Client client) { }

        /// <summary>
        /// 当退出场景的玩家, 当调用此方法后client已经被移出Clients属性
        /// </summary>
        /// <param name="client"></param>
        public virtual void OnExit(Client client) { }

        /// <summary>
        /// 当场景被移除
        /// </summary>
        public virtual void OnRemove() { }

        /// <summary>
        /// 当接收到客户端使用Client.AddOperation方法发送的请求时调用
        /// </summary>
        public virtual void OnOperationSync(Client client, in OperationList operList) => AddOperations(operList.operations);

        /// <summary>
        /// 添加玩家
        /// </summary>
        /// <param name="collection"></param>
        public virtual void AddPlayers(IEnumerable<Client> collection)
        {
            lock (this)
            {
                foreach (var client in collection)
                    AddPlayerInternal(client);
            }
        }

        internal void Tick()
        {
            Event.FixedUpdate(TimeTick);
        }

        private bool UpdateLock()
        {
            Update();
            currFps++;
            return true;
        }

        private bool OnFPS()
        {
            preFps = currFps;
            currFps = 0;
            return true;
        }

        /// <summary>
        /// 网络帧同步, 状态同步更新, 帧时间根据服务器主类的SyncSceneTime属性来调整速率
        /// </summary>
        public virtual void Update()
        {
            var clients = Clients;
            int clientCount = clients.Count;
            if (clientCount <= 0)
                return;
            for (int i = 0; i < clientCount; i++)
                clients[i].OnUpdate();
            if (operations.Count > 0)
            {
                Frame++;
                SendOperitions(operations);
            }
        }

        /// <summary>
        /// 发送操作, 如果超过Split属性则会进行拆分发送
        /// </summary>
        /// <param name="operations"></param>
        public void SendOperitions(FastList<Operation> operations)
        {
            int count = operations.Count;
            while (count > Split)
            {
                OnPacket(Split, Clients, operations);
                count -= Split;
            }
            if (count > 0) OnPacket(count, Clients, operations);
        }

        /// <summary>
        /// 发送操作, 如果超过Split属性则会进行拆分发送
        /// </summary>
        /// <param name="client"></param>
        /// <param name="operations"></param>
        public void SendOperitions(Client client, FastList<Operation> operations)
        {
            int count = operations.Count;
            while (count > Split)
            {
                OnPacket(Split, client, operations);
                count -= Split;
            }
            if (count > 0) OnPacket(count, client, operations);
        }

        /// <summary>
        /// 当封包数据时调用
        /// </summary>
        /// <param name="count"></param>
        public virtual void OnPacket(int count)
        {
            OnPacket(count, Clients, operations);
        }

        /// <summary>
        /// 当封包数据时调用
        /// </summary>
        /// <param name="count"></param>
        /// <param name="clients"></param>
        /// <param name="operations"></param>
        public virtual void OnPacket(int count, FastList<Client> clients, FastList<Operation> operations)
        {
            var opts = operations.GetRemoveRange(0, count);
            var operList = new OperationList(Frame, opts);
            var buffer = Server.SerializeAdapter.OnSerializeOpt(operList);
            Server.Multicast(clients, NetCmd.OperationSync, buffer, false, SyncSerialize.False);
        }

        /// <summary>
        /// 当封包数据时调用
        /// </summary>
        /// <param name="count"></param>
        /// <param name="client"></param>
        /// <param name="operations"></param>
        public virtual void OnPacket(int count, Client client, FastList<Operation> operations)
        {
            var opts = operations.GetRemoveRange(0, count);
            var operList = new OperationList(Frame, opts);
            var buffer = Server.SerializeAdapter.OnSerializeOpt(operList);
            Server.Call(client, NetCmd.OperationSync, buffer, false, SyncSerialize.False);
        }

        /// <summary>
        /// 添加操作帧, 等待帧时间同步发送
        /// </summary>
        /// <param name="opt"></param>
        public virtual void AddOperation(in Operation opt)
        {
            lock (this)
            {
                operations.Add(opt);
            }
        }

        /// <summary>
        /// 添加操作帧, 等待帧时间同步发送
        /// </summary>
        /// <param name="opts"></param>
        public virtual void AddOperations(List<Operation> opts)
        {
            lock (this)
            {
                operations.AddRange(opts);
            }
        }

        /// <summary>
        /// 添加操作帧, 等待帧时间同步发送
        /// </summary>
        /// <param name="opts"></param>
        public virtual void AddOperations(in Operation[] opts)
        {
            lock (this)
            {
                operations.AddRange(opts);
            }
        }

        /// <summary>
        /// 移除玩家
        /// </summary>
        /// <param name="client"></param>
        public void Remove(Client client)
        {
            lock (this)
            {
                if (client.SceneHash != hash)
                    return;
                OnBeginExit(client);
                Clients.Remove(client);
                OnExit(client);
                client.OnExit();
                client.Scene = null;
                client.SceneName = string.Empty;
                client.SceneHash = -1;
            }
        }

        /// <summary>
        /// 移除所有玩家
        /// </summary>
        public void RemoveAll()
        {
            lock (this)
            {
                while (Clients.Count > 0) //解决移除不全的问题
                    Remove(Clients[0]);
                Clients.Clear();
            }
        }

        /// <summary>
        /// 执行移除场景
        /// </summary>
        public void RemoveScene()
        {
            RemoveAll();
            OnRemove();
            Group = null;
        }

        /// <summary>
        /// 移除场景所有玩家操作
        /// </summary>
        public void RemoveOperations()
        {
            lock (this)
            {
                operations.Clear();
            }
        }

        /// <inheritdoc/>
        public override string ToString()
        {
            return $"场景名称:{Name} 场景人数:{Clients.Count}/{Capacity} 操作列表:{operations.Count}";
        }
    }
}